/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */

#ifndef __GAME_ENGINE_H__
#define __GAME_ENGINE_H__

#include "SceneObject.h"

namespace PQEngine
{
	enum RendererType
	{
		OGRE_RS_UNKOWN,
		OGRE_RS_GL,
		OGRE_RS_GLES,
		OGRE_RS_D3D9,
		OGRE_RS_D3D10,
		OGRE_RS_D3D11,
		OGRE_RS_UNKNOWN,
	};

	class UserPreference
	{
	public:
		float animSpeed;
	};

	/* class used to listen engine frame events.
	*/
	class GEngineListener
		: public Ogre::FrameListener
	{
	public:
		GEngineListener();
		~GEngineListener();

		//update scene
		void update(float deltaT);
		bool frameRenderingQueued(const Ogre::FrameEvent& evt);
		void setActiveScene(SceneObject* sceneObject);

	private:
		Ogre::Root* _root;
		SceneObject* _sceneObject;//current scene object need update

	};

	/*Singleton Game Engine class used to manage all scene object
	*/
	class GameEngine
	{
	public:
		static const std::string COMMAND_DATA_TYPE;
		static const std::string MAT_TRANSPRENT;
		static const float OBJECT_FRAME_DISTANCE;

		static const std::string TERRAIN_RESOURCE_GROUP_NAME;
		

		GameEngine(const Ogre::String& homeDir= "");
		~GameEngine();
		void initResource();
		static GameEngine& getSingleton()
		{
			static GameEngine inst;
			return inst;
		}

		//execute sdtp formated comand datagram
		void exec(char* data,int len);
		//add resource to current scene object
		GameObject* addResource(std::string path);
		GameObject* addGPKResource(std::string path);
		
		void addCharacterModel(std::string path);//path in wow archive(mpq archives)

		void add2DSprite(Sprite2D* sprite);

		SceneObject* createSceneObject(Ogre::String hWnd,int width,int height);

		
		//update sceneObject if is activate
		void update(SceneObject* sceneObject,float deltaT)
		{
			if(_sceneObject==sceneObject){
				sceneObject->update(deltaT);
			}
		}

		void update(float deltaT)
		{
			if(_sceneObject){
				_sceneObject->update(deltaT);
			}
		}

		inline void removeSceneObject(SceneObject* sceneObject)
		{
		}
		

		void setActiveScene(SceneObject *sceneObject);

		//There is a problem when multiple render window exist
		//render specific scene
		inline void renderOneFrame(SceneObject* sceneObject)
		{
			if(!sceneObject){
				return;
			}
			if(sceneObject==_sceneObject){
				if(!sceneObject->isActivate()){
					OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR,
						"Bug.SceneObject not actived correctly!","GameEngine::renderOneFrame");
				}
				_root->renderOneFrame();
			}
		}
		inline bool useOpengl()
		{
			if(_rendererType== OGRE_RS_GL || _rendererType == OGRE_RS_GLES){
				return true;
			}
			return false;
		}

		inline RendererType getRendererType()
		{
			return _rendererType;
		}

		inline static Ogre::Real getStepRate()
		{
			return Ogre::Real(1.0) / Ogre::Real(60.0);
		}

		inline static Ogre::Real getTickRate()
		{
			return Ogre::Real(60.0);
		}

		inline static Ogre::Real getAnimRate()
		{
			return _animRate;
		}

		inline static void setAnimRate(Ogre::Real rate)
		{
			_animRate=rate;
		}

		/*Get current scene background color
		*/
		inline Ogre::ColourValue& getBackground()
		{
			assert(_sceneObject);
			return _sceneObject->getRenderProperty().getBackground();
			
		}

		/*Set current scene background color
		*/
		inline void setBackground(Ogre::ColourValue value)
		{
			assert(_sceneObject);
			_sceneObject->getRenderProperty().setBackground(value);
		}

		/*Get current scene fog color
		*/
		inline Ogre::ColourValue& getFogColor()
		{
			assert(_sceneObject);
			return _sceneObject->getRenderProperty().getFogColor();
			
		}

		inline void setFogColor(Ogre::ColourValue value)
		{
			assert(_sceneObject);
			//_sceneObject->getRenderProperty().setFogColor(value);
		}

		inline float getFogDensity()
		{
			assert(_sceneObject);
			return _sceneObject->getRenderProperty().getFogDensity();
		}

		inline void setFogIntensity(float value)
		{
			assert(_sceneObject);
			
		}
		inline void setPolyMode(Ogre::PolygonMode mode)
		{
			assert(_sceneObject);
			_sceneObject->getRenderProperty().setPolyMode(mode);

		}

		inline SceneObject* getSceneObject()
		{
			assert(_sceneObject);
			return _sceneObject;
		}

		inline GameObject* getGameObject()
		{
			assert(_sceneObject);
			return _sceneObject->getGameObject();
		}


	private:
		

	private:
		RendererType _rendererType;

		SceneObject* _sceneObject;//current scene object
		Ogre::Root* _root;
		bool _isExit;
		Ogre::Real _animSpeed;
		
		static Ogre::Real _tickRate;
		static Ogre::Real _animRate;
	public:
		inline void setAnimSpeed(float speed)
		{
			_animSpeed=speed;
		}

		inline Ogre::Real getAnimSpeed()
		{
			return _animSpeed;
		}
		
		/*Select GameObject by mouse position
		*/
		GameObject* selectObject(int x,int y);

		/*Select GameObject by id
		*/
		GameObject* selectObject(int id);

		inline void createSphereObject(Ogre::Vector3 position)
		{
			if(_sceneObject){
				_sceneObject->createSphereObject(position);
			}
		}

		inline void startEditTerrain()
		{
			if(_sceneObject){
				_sceneObject->startEditTerrain();
			}
			
		}

		inline void stopEditTerrain()
		{
			if(_sceneObject){
				_sceneObject->stopEditTerrain();
			}
			
		}

		inline void addTerrainLayer(std::string textureName)
		{
			if(_sceneObject){
				_sceneObject->addTerrainLayer(textureName);
			}
		}
		
		inline void selectTerrainLayer(int layer)
		{
			if(_sceneObject){
				_sceneObject->selectTerrainLayer(layer);
			}
		}

		inline void setTerrainEditMode(TerrainEditMode mode)
		{
			if(_sceneObject){
				_sceneObject->setTerrainEditMode(mode);
			}
		}

		inline void selectTerrainBrush(int index)
		{
			if(_sceneObject){
				_sceneObject->selectTerrainBrush(index);
			}
		}

		/*Get the terrain group object.
		@Note Each scene can only have one such group.
		*/
		inline TerrainGroupObject* getTerrainGroup()
		{
			if(_sceneObject){
				return _sceneObject->getTerrainGroup();
			}
			return 0;
		}

		inline void selectTerrain(int index)
		{
			if(_sceneObject){
				_sceneObject->selectTerrain(index);
			}
		}
		
		/*Add terrain layer texture file from local file system according to file path.
		*/
		inline bool addTerrainTextureFromLocalFS(std::string filePath)
		{
			if(_sceneObject){
				return _sceneObject->addTerrainTextureFromLocalFS(filePath);
			}
			return false;
		}

		/*Add terrain brush alpha texture file from local file system according to file path.
		*/
		inline bool addTerrainBrushTextureFromLocalFS(std::string filePath)
		{
			if(_sceneObject){
				return _sceneObject->addTerrainBrushTextureFromLocalFS(filePath);
			}
			return false;
		}
		
		/*Return the number of terrain layer texture.
		*/
		inline int getNumTerrainTexture()
		{
			if(_sceneObject){
				return _sceneObject->getNumTerrainTexture();
			}
			return 0;
		}
		
		/*Return the number of terrain brush texture(alpha image).
		*/
		inline int getNumTerrainBrushTexture()
		{
			if(_sceneObject){
				return _sceneObject->getNumTerrainBrushTexture();
			}
			return 0;
		}

		/*Return the name of terrain layer texture.
		*/
		inline Ogre::String getTerrainTextureName(int index)
		{
			if(_sceneObject){
				return _sceneObject->getTerrainTextureName(index);
			}

			return "";
		}
		
		/*Return the name of terrain brush texture(alpha image).
		*/
		inline Ogre::String getTerrainBrushTextureName(int index)
		{
			if(_sceneObject){
				return _sceneObject->getTerrainBrushTextureName(index);
			}

			return "";
		}


		inline GPKFile* getTerrainTextureFile(int index)
		{
			if(_sceneObject){
				return _sceneObject->getTerrainTextureFile(index);
			}
			return 0;
		}

		
		inline GPKFile* getTerrainBrushTextureFile(int index)
		{
			if(_sceneObject){
				return _sceneObject->getTerrainBrushTextureFile(index);
			}
			return 0;
		}


		inline GPKFile* getTerrainTextureFile(std::string name)
		{
			if(_sceneObject){
				return _sceneObject->getTerrainTextureFile(name);
			}
			return 0;
		}
		
		inline GPKFile* getTerrainBrushTextureFile(std::string name)
		{
			if(_sceneObject){
				return _sceneObject->getTerrainBrushTextureFile(name);
			}
			return 0;
		}

	};
}
#define SEngine PQEngine::GameEngine::getSingleton() 
#endif
